home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / c_news / 13 / proj1.c < prev    next >
C/C++ Source or Header  |  1988-12-27  |  48KB  |  1,729 lines

  1. /* PROJ1.C
  2.  *
  3.  * Scott R. Houck
  4.  * Written in Turbo C 2.0
  5.  *
  6.  * Add egavga.bgi to graphics.lib.  Then compile with:
  7.  *
  8.  *      tcc proj1 graphics.lib
  9.  *
  10.  * This is a simple interactive graphics program.  Two symbols exist:
  11.  * a square and a triangle.  Operations are:
  12.  *
  13.  *      create a symbol
  14.  *      rotate a symbol
  15.  *      move a symbol
  16.  *      delete a symbol
  17.  *      delete all symbols
  18.  *      print data structure
  19.  *      terminate program
  20.  *
  21.  * Assume that your "room" is square, 15 feet on each side.  The symbol
  22.  * called "square" is one foot on each side, while the triangle is one
  23.  * foot on a side.
  24.  *
  25.  * The following Turbo C graphics functions are used in this program:
  26.  *
  27.  *   cleardevice         Clears the screen (active page)
  28.  *   clearviewport       Clears the current viewport
  29.  *   closegraph          Shuts down the graphics system
  30.  *   drawpoly            Draws the outline of a polygon
  31.  *   floodfill           Flood-fills a bounded region
  32.  *   getcolor            Returns the current drawing color
  33.  *   getfillsettings     Returns information about the current fill settings
  34.  *   getimage            Saves a bit image of the specified region to memory
  35.  *   getmaxx             Returns the current x resolution
  36.  *   getmaxy             Returns the current y resolution
  37.  *   getviewsettings     Returns information about the current viewport
  38.  *   grapherrormsg       Returns an error message for specified error code
  39.  *   graphresult         Returns an error code for last error
  40.  *   imagesize           Retuns number of bytes needed to store an image
  41.  *   initgraph           Initializes the graphics system
  42.  *   lineto              Draws a line from the current position to (x,y)
  43.  *   moveto              Moves the current position to (x,y)
  44.  *   outtext             Sends a string to the screen at the current position
  45.  *   putimage            Puts a previously saved bit image onto the screen
  46.  *   rectangle           Draws a rectangle
  47.  *   registerbgidriver   Registers a driver file for inclusion at link time
  48.  *   setactivepage       Sets the active page for graphics output
  49.  *   setcolor            Sets the current drawing color
  50.  *   setfillstyle        Sets the fill pattern and fill color
  51.  *   settextjustify      Set text justification values used by outtext
  52.  *   setviewport         Sets the current output viewport for graphics output
  53.  *   setvisualpage       Sets the visual graphics page number
  54.  */
  55.  
  56. #include <stdio.h>
  57. #include <math.h>
  58. #include <dos.h>
  59. #include <mem.h>
  60. #include <alloc.h>
  61. #include <graphics.h>
  62.  
  63.  
  64. /* Mouse buttons */
  65.  
  66. #define LEFTBUTTON   1
  67. #define RIGHTBUTTON  2
  68.  
  69. /* Clipping */
  70.  
  71. #define CLIPPING_ON  1
  72. #define CLIPPING_OFF 0
  73.  
  74. /* Boolean values */
  75.  
  76. #define TRUE   1
  77. #define FALSE  0
  78.  
  79. /* Dialog box options */
  80.  
  81. #define YES    1
  82. #define NO     0
  83.  
  84. /* Symbols */
  85.  
  86. #define TRIANGLE 0
  87. #define SQUARE   1
  88.  
  89. /* Menu options */
  90.  
  91. #define IGNORE       -1
  92. #define CREATE       0
  93. #define DELETE       1
  94. #define MOVE         2
  95. #define ROTATE       3
  96. #define DELETE_ALL   4
  97. #define PRINT_DATA   5
  98. #define CANCEL       6
  99. #define QUIT         7
  100.  
  101.  
  102. /* Some useful typedefs */
  103.  
  104. typedef struct {
  105.    double x, y;
  106. } DPOINT;         /* A point with x and y as doubles */
  107.  
  108. typedef struct pointtype   POINT;         /* A point with x and y as ints */
  109. typedef POINT              EXTENT [2];    /* Rectangular extent */
  110. typedef double             MATRIX [3][3]; /* A 3x3 matrix */
  111. typedef double             VECTOR [3];    /* A 1x3 matrix */
  112.  
  113.  
  114. /* Menu Box structure */
  115.  
  116. struct {
  117.    EXTENT extent;
  118.    char *text;
  119. } menuBox[8];
  120.  
  121.  
  122. /* Object structure */
  123.  
  124. typedef struct {
  125.    int    numverts;  /* Number of vertices */
  126.    DPOINT vertex[4]; /* At most 4 vertices */
  127. } OBJECT;
  128.  
  129.  
  130. /* For linked list of objects */
  131.  
  132. typedef struct node   NODE, *NODEPTR;
  133.  
  134. struct node {
  135.    OBJECT  object;
  136.    NODEPTR next;
  137. };
  138.  
  139.  
  140. /* Function prototypes */
  141.  
  142. void    InitializeGraphics(void);
  143. void    DrawScreen(void);
  144. void    Beep(void);
  145. int     MouseReset(int *);
  146. void    MouseOn(void);
  147. void    MouseOff(void);
  148. int     MouseStatus(POINT *);
  149. void    MouseWaitForPress(int, POINT *);
  150. void    MouseWaitForRelease(int, POINT *);
  151. void    MouseSetHorizPos(int, int);
  152. void    MouseSetVertPos(int, int);
  153. void    MouseRestrict(void);
  154. void    MouseFree(void);
  155. void    MMmult(MATRIX, MATRIX, MATRIX);
  156. void    VMmult(VECTOR, MATRIX, VECTOR);
  157. void    CalcW2Vmatrix(double, double, double, double,
  158.            int, int, int, int, MATRIX);
  159. void    CalcV2Wmatrix(int, int, int, int,
  160.            double, double, double, double, MATRIX);
  161. void    DrawObject(OBJECT);
  162. void    Translate(VECTOR, double, double, VECTOR);
  163. void    TranslateObject(OBJECT *, double, double);
  164. void    Rotate(VECTOR, double, DPOINT, VECTOR);
  165. void    RotateObject(OBJECT *, double);
  166. int     PointInExtent(POINT, EXTENT);
  167. int     PickCorrInMenu(POINT);
  168. int     HandlePickInMenu(POINT);
  169. void    Message(char *);
  170. void    HighlightMenu(int, int, int);
  171. void    HighlightSymbol(int, int, int);
  172. int     DialogBox(void);
  173. NODEPTR AddList(OBJECT);
  174. void    DeleteList(NODEPTR);
  175. NODEPTR SearchList(POINT);
  176. void    DoCreateObject(void);
  177. void    DoDeleteObject(void);
  178. void    DoMoveObject(void);
  179. void    DoRotateObject(void);
  180. void    DoDeleteAll(void);
  181. void    DoPrintData(void);
  182. void    DoCancel(void);
  183. int     DoQuit(void);
  184.  
  185.  
  186. /* Global variables */
  187.  
  188. MATRIX W2V;                            /* Window to Viewport matrix */
  189. MATRIX V2W;                            /* Viewport to Window matrix */
  190. int maxx, maxy;                        /* Maximum pixel values */
  191. int mminx, mminy, mmaxx, mmaxy;        /* Message area coordinates */
  192. int vminx, vminy, vmaxx, vmaxy;        /* "Room" (viewport) coords */
  193. int dminx, dminy, dmaxx, dmaxy;        /* Dialog box coordinates */
  194. int triangleSymbol[8];                 /* Triangle symbol */
  195. int squareSymbol[10];                  /* Square symbol */
  196. EXTENT triangleExtent;                 /* Extent of the triangle symbol */
  197. EXTENT squareExtent;                   /* Extent of the square symbol */
  198. EXTENT roomExtent;                     /* Extent of the room */
  199. EXTENT yesExtent, noExtent;            /* Extent of the YES/NO buttons */
  200. POINT triangleCenter, squareCenter;    /* Centers of the symbols */
  201. NODEPTR list = NULL;                   /* Pointer to the linked list */
  202. NODEPTR listtail = NULL;               /* Pointer to the end of the list */
  203. unsigned dialogSize;                   /* Dialog box image size */
  204. void *dialogBuffer, *saveBuffer;       /* Dialog box and screen buffers */
  205.  
  206.  
  207. main()
  208. {
  209.    POINT position;
  210.    int choice;
  211.  
  212.    InitializeGraphics();
  213.    DrawScreen();
  214.    MouseOn();
  215.  
  216.    do
  217.       {
  218.       Message("Choose a menu box with the left mouse button");
  219.       MouseWaitForPress(LEFTBUTTON, &position);
  220.       MouseWaitForRelease(LEFTBUTTON, &position);
  221.       choice = HandlePickInMenu(position);
  222.       }
  223.    while (choice != QUIT);
  224.  
  225.    MouseOff();
  226.    closegraph();
  227. }
  228.  
  229.  
  230. /* InitializeGraphics() initializes the graphics package and mouse driver.
  231.  * If there is an error, the program aborts with an appropriate error
  232.  * message.
  233.  */
  234. void InitializeGraphics()
  235. {
  236.    int graphDriver, graphMode, errorCode;
  237.    int numberOfButtons;
  238.  
  239.    if (MouseReset(&numberOfButtons) == 0)
  240.       {
  241.       printf("Mouse driver is not installed\n");
  242.       exit(1);
  243.       }
  244.  
  245.    if (numberOfButtons < 2)
  246.       {
  247.       printf("This program requires a mouse with at least two buttons\n");
  248.       exit(2);
  249.       }
  250.  
  251.    if (registerbgidriver(EGAVGA_driver) < 0)
  252.       exit(1);
  253.  
  254.    graphDriver = DETECT;
  255.    initgraph(&graphDriver, &graphMode, "");
  256.    errorCode = graphresult();
  257.    if (errorCode != grOk)
  258.       {
  259.       printf("Graphics System Error: %s\n", grapherrormsg(errorCode));
  260.       exit(1);
  261.       }
  262. }
  263.  
  264.  
  265. /* DrawScreen() draws the screen.  Coordinates are calculated based on
  266.  * the current screen resolution so that the program will run on either
  267.  * a VGA or EGA system without modification.
  268.  */
  269. void DrawScreen()
  270. {
  271.    int vwidth, vheight;                   /* Viewport */
  272.    int bminx, bminy, bmaxx, bmaxy;        /* Menu buttons */
  273.    int bwidth, bheight, bgap, bstart;     /* Menu buttons */
  274.    int dwidth, dheight;                   /* Dialog box */
  275.    int yminx, yminy, ymaxx, ymaxy;        /* YES button */
  276.    int nminx, nminy, nmaxx, nmaxy;        /* NO button */
  277.    int i;
  278.    static char *boxText[] = { "CREATE", "DELETE", "MOVE", "ROTATE",
  279.       "DELETE ALL", "PRINT DATA", "CANCEL", "QUIT" };
  280.  
  281.    /* Set the pages to draw the screen in the background */
  282.  
  283.    setactivepage(0);
  284.    setvisualpage(1);
  285.  
  286.    /* Get maximum x and y screen coordinates */
  287.  
  288.    maxx = getmaxx();
  289.    maxy = getmaxy();
  290.  
  291.    /* Calculate the coordinates of the room */
  292.  
  293.    roomExtent[0].x = vminx = (int)(0.30 * maxx);
  294.    roomExtent[0].y = vminy = (int)(0.05 * maxy);
  295.    roomExtent[1].x = vmaxx = (int)(0.95 * maxx);
  296.    roomExtent[1].y = vmaxy = (int)(0.80 * maxy);
  297.  
  298.    vwidth  = vmaxx - vminx;
  299.    vheight = vmaxy - vminy;
  300.  
  301.    /* Calculate the coordinates of the message area */
  302.  
  303.    mminx = vminx;
  304.    mminy = (int)(0.85 * maxy);
  305.    mmaxx = vmaxx;
  306.    mmaxy = (int)(0.95 * maxy);
  307.  
  308.    /* Draw the dialog box */
  309.  
  310.    dwidth  = (int)(0.35 * vwidth);
  311.    dheight = (int)(0.25 * vheight);
  312.    dminx   = vminx + (vwidth - dwidth) / 2;
  313.    dminy   = vminy + (vheight - dheight) / 2;
  314.    dmaxx   = dminx + dwidth;
  315.    dmaxy   = dminy + dheight;
  316.    rectangle(dminx, dminy, dmaxx, dmaxy);    /* The outline */
  317.  
  318.    yesExtent[0].x = yminx = dminx + (int)(0.10 * dwidth);
  319.    yesExtent[0].y = yminy = dminy + (int)(0.60 * dheight);
  320.    yesExtent[1].x = ymaxx = dminx + (int)(0.40 * dwidth);
  321.    yesExtent[1].y = ymaxy = dminy + (int)(0.90 * dheight);
  322.    rectangle(yminx, yminy, ymaxx, ymaxy);
  323.    setfillstyle(SOLID_FILL, RED);
  324.    floodfill(yminx+1, yminy+1, WHITE);  /* YES button */
  325.  
  326.    noExtent[0].x = nminx = dminx + (int)(0.60 * dwidth);
  327.    noExtent[0].y = nminy = yminy;
  328.    noExtent[1].x = nmaxx = dminx + (int)(0.90 * dwidth);
  329.    noExtent[1].y = nmaxy = ymaxy;
  330.    rectangle(nminx, nminy, nmaxx, nmaxy);
  331.    floodfill(nminx+1, nminy+1, WHITE);  /* NO button */
  332.  
  333.    setfillstyle(SOLID_FILL, LIGHTGRAY);
  334.    floodfill(dminx+1, dminy+1, WHITE);  /* The interior */
  335.  
  336.    settextjustify(CENTER_TEXT, CENTER_TEXT);
  337.    moveto(dminx+dwidth/2, dminy+(yminy-dminy)/2);
  338.    setcolor(BLACK);
  339.    outtext("Are you sure?");
  340.    setcolor(WHITE);
  341.    moveto((yminx+ymaxx)/2, (yminy+ymaxy)/2);
  342.    outtext("YES");
  343.    moveto((nminx+nmaxx)/2, (nminy+nmaxy)/2);
  344.    outtext("NO");
  345.    setcolor(WHITE);
  346.  
  347.    /* Save dialog box in memory */
  348.  
  349.    dialogSize = imagesize(dminx, dminy, dmaxx, dmaxy);
  350.    dialogBuffer = malloc(dialogSize);
  351.    getimage(dminx, dminy, dmaxx, dmaxy, dialogBuffer);
  352.    cleardevice(); /* Clear the screen */
  353.  
  354.    /* Draw the menu buttons */
  355.  
  356.    bheight = (int)(0.06 * maxy);
  357.    bwidth  = (int)(0.15 * maxx);
  358.    bgap    = (int)(0.03 * maxy);
  359.    bstart  = (int)(0.05 * maxy);
  360.    setfillstyle(SOLID_FILL, RED);
  361.    bminx = (int)(0.05 * maxx);
  362.    for (i = 0; i < 8; i++)
  363.       {
  364.       bminy = (int)(i * (bheight + bgap)) + bstart;
  365.       bmaxx = bminx + bwidth;
  366.       bmaxy = bminy + bheight;
  367.       rectangle(bminx, bminy, bmaxx, bmaxy);
  368.       floodfill(bminx+1, bminy+1, WHITE);
  369.       moveto(bminx+bwidth/2, bminy+bheight/2);
  370.       outtext(boxText[i]);
  371.  
  372.       /* Initialize the menuBox info */
  373.  
  374.       menuBox[i].extent[0].x = bminx;
  375.       menuBox[i].extent[0].y = bminy;
  376.       menuBox[i].extent[1].x = bmaxx;
  377.       menuBox[i].extent[1].y = bmaxy;
  378.       menuBox[i].text = boxText[i];
  379.       }
  380.  
  381.    /* Draw the triangle symbol */
  382.  
  383.    bminx   = (int)(0.03 * maxx);
  384.    bminy   = (int)(0.85 * maxy);
  385.    bmaxy   = (int)(0.95 * maxy);
  386.    bwidth  = (int)(0.07 * maxx);
  387.    bheight = bmaxy - bminy;
  388.  
  389.    triangleSymbol[0] = bminx;
  390.    triangleSymbol[1] = bmaxy;
  391.    triangleSymbol[2] = bminx + bwidth;
  392.    triangleSymbol[3] = bmaxy;
  393.    triangleSymbol[4] = bminx + bwidth/2;
  394.    triangleSymbol[5] = bminy;
  395.    triangleSymbol[6] = bminx;
  396.    triangleSymbol[7] = bmaxy;
  397.    triangleCenter.x  = bminx + bwidth/2;
  398.    triangleCenter.y  = bminy + bheight/2;
  399.    drawpoly(4, triangleSymbol);
  400.    setfillstyle(SOLID_FILL, MAGENTA);
  401.    floodfill(bminx + bwidth/2, bminy + bheight/2, WHITE);
  402.  
  403.    /* Initialize the triangle symbol's extent */
  404.  
  405.    triangleExtent[0].x = bminx;
  406.    triangleExtent[0].y = bminy;
  407.    triangleExtent[1].x = bminx + bwidth;
  408.    triangleExtent[1].y = bmaxy;
  409.  
  410.    /* Draw the square symbol */
  411.  
  412.    bgap = (int)(0.05 * maxx);
  413.    bminx += bwidth + bgap;
  414.  
  415.    squareSymbol[0] = bminx;
  416.    squareSymbol[1] = bmaxy;
  417.    squareSymbol[2] = bminx + bwidth;
  418.    squareSymbol[3] = bmaxy;
  419.    squareSymbol[4] = bminx + bwidth;
  420.    squareSymbol[5] = bminy;
  421.    squareSymbol[6] = bminx;
  422.    squareSymbol[7] = bminy;
  423.    squareSymbol[8] = bminx;
  424.    squareSymbol[9] = bmaxy;
  425.    squareCenter.x  = bminx + bwidth/2;
  426.    squareCenter.y  = bminy + bheight/2;
  427.    drawpoly(5, squareSymbol);
  428.    setfillstyle(SOLID_FILL, GREEN);
  429.    floodfill(bminx + bwidth/2, bminy + bheight/2, WHITE);
  430.  
  431.    /* Initialize the square symbol's extent */
  432.  
  433.    squareExtent[0].x = bminx;
  434.    squareExtent[0].y = bminy;
  435.    squareExtent[1].x = bminx + bwidth;
  436.    squareExtent[1].y = bmaxy;
  437.  
  438.    /* Calculate window-to-viewport and viewport-to-window matrices */
  439.  
  440.    CalcW2Vmatrix(0.0, 0.0, 15.0, 15.0, vminx, vminy, vmaxx, vmaxy, W2V);
  441.    CalcV2Wmatrix(vminx, vminy, vmaxx, vmaxy, 0.0, 0.0, 15.0, 15.0, V2W);
  442.  
  443.    /* Draw the message area */
  444.  
  445.    setfillstyle(SOLID_FILL, CYAN);
  446.    rectangle(mminx, mminy, mmaxx, mmaxy);
  447.    floodfill(mminx+1, mminy+1, WHITE);
  448.  
  449.    /* Draw the room */
  450.  
  451.    setfillstyle(SOLID_FILL, BLUE);
  452.    rectangle(vminx-1, vminy-1, vmaxx+1, vmaxy+1);
  453.    floodfill(vminx, vminy, WHITE);
  454.  
  455.    /* Set viewport to the room */
  456.  
  457.    setviewport(vminx, vminy, vmaxx, vmaxy, CLIPPING_ON);
  458.  
  459.    /* Display screen */
  460.  
  461.    setvisualpage(0);
  462. }
  463.  
  464.  
  465. /* Beep() sounds a beep to indicate an error. */
  466.  
  467. void Beep()
  468. {
  469.    sound(150);
  470.    delay(75);
  471.    nosound();
  472. }
  473.  
  474.  
  475. /* MouseReset() returns the current status of the mouse hardware and
  476.  * software.  The mouse status is 0 if the mouse hardware and software
  477.  * are not installed or -1 if the hardware and software are installed.
  478.  *
  479.  * This function also resets the mouse driver to the default values.
  480.  * The number of buttons is returned in numberOfButtons.
  481.  */
  482. int MouseReset(int *numberOfButtons)
  483. {
  484.    union REGS inregs, outregs;
  485.  
  486.    inregs.x.ax = 0;  /* Mouse Function 0 -- Mouse Reset and Status */
  487.    int86(0x33, &inregs, &outregs);
  488.    *numberOfButtons = outregs.x.bx;
  489.    return outregs.x.ax;
  490. }
  491.  
  492.  
  493. /* MouseOn() shows the mouse cursor. */
  494.  
  495. void MouseOn()
  496. {
  497.    union REGS inregs, outregs;
  498.  
  499.    inregs.x.ax = 1;  /* Mouse Function 1 -- Show Cursor */
  500.    int86(0x33, &inregs, &outregs);
  501. }
  502.  
  503.  
  504. /* MouseOff() hides the mouse cursor. */
  505.  
  506. void MouseOff()
  507. {
  508.    union REGS inregs, outregs;
  509.  
  510.    inregs.x.ax = 2;  /* Mouse function 2 -- Hide Cursor */
  511.    int86(0x33, &inregs, &outregs);
  512. }
  513.  
  514.  
  515. /* MouseStatus() returns the state of the left and right mouse buttons
  516.  * and the horizontal and vertical coordinates of the cursor.
  517.  *
  518.  * The button status is a single integer value.  Bit 0 represents the
  519.  * left button; bit 1 represents the right button.  These bits are 1
  520.  * if the corresponding button is down, and 0 if it is up.
  521.  */
  522. int MouseStatus(POINT *position)
  523. {
  524.    union REGS inregs, outregs;
  525.  
  526.    inregs.x.ax = 3;  /* Mouse function 3 --
  527.                         Get Button Status and Mouse Position */
  528.    int86(0x33, &inregs, &outregs);
  529.    position->x = outregs.x.cx;
  530.    position->y = outregs.x.dx;
  531.    return outregs.x.bx; /* Button status */
  532. }
  533.  
  534.  
  535. /* MouseWaitForPress() puts the program in a wait state until the
  536.  * user presses the specified mouse button.
  537.  */
  538. void MouseWaitForPress(int button, POINT *posptr)
  539. {
  540.    int buttonPressed;
  541.  
  542.    do
  543.       buttonPressed = MouseStatus(posptr);
  544.    while (!(buttonPressed & button));
  545. }
  546.  
  547.  
  548. /* MouseWaitForRelease() puts the program in a wait state until the
  549.  * user releases the specified mouse button.
  550.  */
  551. void MouseWaitForRelease(int button, POINT *posptr)
  552. {
  553.    int buttonPressed;
  554.  
  555.    do
  556.       buttonPressed = MouseStatus(posptr);
  557.    while (buttonPressed & button);
  558. }
  559.  
  560.  
  561. /* MouseSetHorizPos() sets the minimum and maximum horizontal cursor
  562.  * coordinates on the screen.  All cursor movement after the call
  563.  * is restricted to the specified area.
  564.  */
  565. void MouseSetHorizPos(int minpos, int maxpos)
  566. {
  567.    union REGS inregs, outregs;
  568.  
  569.    inregs.x.ax = 7;  /* Mouse function 7 -- Set Minimum and Maximum
  570.                         Horizontal Cursor Position */
  571.    inregs.x.cx = minpos;
  572.    inregs.x.dx = maxpos;
  573.    int86(0x33, &inregs, &outregs);
  574. }
  575.  
  576.  
  577. /* MouseSetVertPos() sets the minimum and maximum vertical cursor
  578.  * coordinates on the screen.  All cursor movement after the call
  579.  * is restricted to the specified area.
  580.  */
  581. void MouseSetVertPos(int minpos, int maxpos)
  582. {
  583.    union REGS inregs, outregs;
  584.  
  585.    inregs.x.ax = 8;  /* Mouse function 8 -- Set Minimum and Maximum
  586.                         Vertical Cursor Position */
  587.    inregs.x.cx = minpos;
  588.    inregs.x.dx = maxpos;
  589.    int86(0x33, &inregs, &outregs);
  590. }
  591.  
  592.  
  593. /* MouseRestrict() restricts the cursor to the viewport. */
  594.  
  595. void MouseRestrict()
  596. {
  597.    MouseSetHorizPos(vminx, vmaxx);
  598.    MouseSetVertPos(vminy, vmaxy);
  599. }
  600.  
  601.  
  602. /* MouseFree() frees the cursor to move about the entire screen. */
  603.  
  604. void MouseFree()
  605. {
  606.    MouseSetHorizPos(0, maxx);
  607.    MouseSetVertPos(0, maxy);
  608. }
  609.  
  610.  
  611. /* MMmult() performs Matrix-Matrix multiplication.
  612.  *
  613.  *   _             _       _             _       _             _
  614.  *  |               |     |               |     |               |
  615.  *  | a    a    a   |     | b    b    b   |     | c    c    c   |
  616.  *  |  00   01   02 |     |  00   01   02 |     |  00   01   02 |
  617.  *  |               |     |               |     |               |
  618.  *  | a    a    a   |     | b    b    b   |     | c    c    c   |
  619.  *  |  10   11   12 |  x  |  10   11   12 |  =  |  10   11   12 |
  620.  *  |               |     |               |     |               |
  621.  *  | a    a    a   |     | b    b    b   |     | c    c    c   |
  622.  *  |  20   21   22 |     |  20   21   22 |     |  20   21   22 |
  623.  *  |_             _|     |_             _|     |_             _|
  624.  *
  625.  *
  626.  */
  627. void MMmult(MATRIX a, MATRIX b, MATRIX c)
  628. {
  629.    int i, j, k;
  630.  
  631.    for (i = 0; i < 3; i++)       /* Row i */
  632.       for (j = 0; j < 3; j++)    /* Column j */
  633.          for (c[i][j] = 0.0, k = 0; k < 3; k++)
  634.             c[i][j] += a[i][k] * b[k][j];
  635. }
  636.  
  637.  
  638. /* VMmult() performs Vector-Matrix multiplication.
  639.  *
  640.  *                         _             _ 
  641.  *                        |               |
  642.  *                        | b    b    b   |
  643.  *    _            _      |  00   01   02 |      _            _ 
  644.  *   |              |     |               |     |              |
  645.  *   |  a   a   a   |     | b    b    b   |     |  c   c   c   |
  646.  *   |   0   1   2  |  x  |  10   11   12 |  =  |   0   1   2  |
  647.  *   |_            _|     |               |     |_            _|
  648.  *                        | b    b    b   |
  649.  *                        |  20   21   22 |
  650.  *                        |_             _|
  651.  *
  652.  *
  653.  */
  654. void VMmult(VECTOR a, MATRIX b, VECTOR c)
  655. {
  656.    int i, j;
  657.  
  658.    for (i = 0; i < 3; i++)
  659.       for (c[i] = 0.0, j = 0; j < 3; j++)
  660.          c[i] += a[j] * b[j][i];
  661. }
  662.  
  663.  
  664. /* CalcW2Vmatrix() calculates the Window to Viewport matrix.
  665.  *
  666.  * Window coordinates:   (xmin,ymin) and (xmax,ymax)
  667.  * Viewport coordinates: (umin,vmin) and (umax,vmax)
  668.  *
  669.  *   _              _  _                         _  _               _ 
  670.  *  |                ||                           ||                 |
  671.  *  |                || umax-umin                 ||                 |
  672.  *  |    1    0    0 || ---------       0       0 || 1      0      0 |
  673.  *  |                || xmax-xmin                 ||                 |
  674.  *  |                ||             vmax-vmin     ||                 |
  675.  *  |    0    1    0 ||   0       - ---------   0 || 0      1      0 |
  676.  *  |                ||             ymax-ymin     ||                 |
  677.  *  |                ||                           ||                 |
  678.  *  | -xmin -ymin  1 ||   0             0       1 || 0  vmax-vmin  1 |
  679.  *  |                ||                           ||                 |
  680.  *  |_              _||_                         _||_               _|
  681.  *
  682.  *
  683.  *   trans to origin    scale window to viewport    trans to viewport
  684.  *
  685.  *
  686.  *
  687.  *  Note that these matrices are somewhat different from the normal
  688.  *  matrices used because the Turbo C graphics package considers the
  689.  *  origin to be at the upper left corner of the screen (rather than
  690.  *  the lower left) and also because the setviewport() function changes
  691.  *  the logical coordinates of the viewport to (0,0).
  692.  */
  693.  
  694. void CalcW2Vmatrix(xmin, ymin, xmax, ymax, umin, vmin, umax, vmax, m)
  695. double xmin, ymin, xmax, ymax;   /* Window coordinates */
  696. int    umin, vmin, umax, vmax;   /* Viewport coordinates */
  697. MATRIX m;                        /* Resulting Window to Viewport matrix */
  698. {
  699.    MATRIX a, b;
  700.  
  701.    /* Translate window to origin */
  702.  
  703.    a[0][0] = a[1][1] = a[2][2] = 1.0;
  704.    a[0][1] = a[0][2] = a[1][0] = a[1][2] = 0.0;
  705.    a[2][0] = -xmin;
  706.    a[2][1] = -ymin;
  707.  
  708.    /* Scale window into viewport */
  709.  
  710.    m[0][1] = m[0][2] = m[1][0] = m[1][2] = m[2][0] = m[2][1] = 0.0;
  711.    m[2][2] = 1.0;
  712.    m[0][0] =  (double)(umax - umin) / (xmax - xmin);
  713.    m[1][1] = -(double)(vmax - vmin) / (ymax - ymin);
  714.  
  715.    MMmult(a, m, b);
  716.  
  717.    /* Translate to viewport position
  718.     *
  719.     * Normally, you would translate back to (umin,vmin), but Turbo C's
  720.     * setviewport() function treats the upper left corner of the viewport
  721.     * as (0,0).  We want the origin to be in the lower left corner, so we
  722.     * still have to translate to viewport_height - y.  We scaled by -1
  723.     * above (to get the -y), so all we have to do is translate by the
  724.     * viewport height (vmax-vmin).
  725.     */
  726.  
  727.    a[2][0] = 0.0;
  728.    a[2][1] = (double)(vmax - vmin);
  729.  
  730.    MMmult(b, a, m);
  731. }
  732.  
  733.  
  734. /* CalcV2Wmatrix() calculates the Viewport to Window matrix.
  735.  *
  736.  * Viewport coordinates: (umin,vmin) and (umax,vmax)
  737.  * Window coordinates:   (xmin,ymin) and (xmax,ymax)
  738.  *
  739.  *   _                  _  _                         _  _             _ 
  740.  *  |                    ||                           ||               |
  741.  *  |                    || xmax-xmin                 ||               |
  742.  *  | 1       0        0 || ---------       0       0 ||  1     0    0 |
  743.  *  |                    || umax-umin                 ||               |
  744.  *  |                    ||             ymax-ymin     ||               |
  745.  *  | 0       1        0 ||   0       - ---------   0 ||  0     1    0 |
  746.  *  |                    ||             vmax-vmin     ||               |
  747.  *  |                    ||                           ||               |
  748.  *  | 0  -(vmax-vmin)  1 ||   0             0       1 || xmin  ymin  1 |
  749.  *  |                    ||                           ||               |
  750.  *  |_                  _||_                         _||_             _|
  751.  *
  752.  *
  753.  *     trans to origin      scale viewport to window    trans to window
  754.  *
  755.  *
  756.  *
  757.  *  Note that these matrices are somewhat different from the normal
  758.  *  matrices used because the Turbo C graphics package considers the
  759.  *  origin to be at the upper left corner of the screen (rather than
  760.  *  the lower left) and also because the setviewport() function changes
  761.  *  the logical coordinates of the viewport to (0,0).
  762.  */
  763.  
  764. void CalcV2Wmatrix(umin, vmin, umax, vmax, xmin, ymin, xmax, ymax, m)
  765. int    umin, vmin, umax, vmax;   /* Viewport coordinates */
  766. double xmin, ymin, xmax, ymax;   /* Window coordinates */
  767. MATRIX m;                        /* Resulting Viewport to Window matrix */
  768. {
  769.    MATRIX a, b;
  770.  
  771.    /* Translate viewport to origin */
  772.  
  773.    a[0][0] = a[1][1] = a[2][2] = 1.0;
  774.    a[0][1] = a[0][2] = a[1][0] = a[1][2] = a[2][0] = 0.0;
  775.    a[2][1] = -(double)(vmax - vmin);
  776.  
  777.    /* Scale viewport to window */
  778.  
  779.    m[0][1] = m[0][2] = m[1][0] = m[1][2] = m[2][0] = m[2][1] = 0.0;
  780.    m[2][2] = 1.0;
  781.    m[0][0] =  (xmax - xmin) / (double)(umax - umin);
  782.    m[1][1] = -(ymax - ymin) / (double)(vmax - vmin);
  783.  
  784.    MMmult(a, m, b);
  785.  
  786.    /* Translate to window position */
  787.  
  788.    a[2][0] = xmin;
  789.    a[2][1] = ymin;
  790.  
  791.    MMmult(b, a, m);
  792. }
  793.  
  794.  
  795. /* DrawObject() draws the specified object on the screen. */
  796.  
  797. void DrawObject(OBJECT object)
  798. {
  799.    VECTOR p, q;
  800.    int i;
  801.  
  802.    p[2] = 1.0;
  803.  
  804.    /* We will loop one more than the number of vertices in order
  805.     * to draw a line from the last vertex to the first vertex.
  806.     */
  807.    for (i = 0; i < object.numverts + 1; i++)
  808.       {
  809.       p[0] = object.vertex[i % object.numverts].x;
  810.       p[1] = object.vertex[i % object.numverts].y;
  811.       VMmult(p, W2V, q);   /* Transform to viewport coords */
  812.       if (i == 0)
  813.          moveto((int)q[0], (int)q[1]);
  814.       else
  815.          lineto((int)q[0], (int)q[1]);
  816.       }
  817. }
  818.  
  819.  
  820. /* Translate() translates a point contained in VECTOR p by the amounts
  821.  * Dx and Dy.  The new point is contained in VECTOR q.
  822.  *
  823.  *                         _             _ 
  824.  *                        |               |
  825.  *                        |  1    0    0  |
  826.  *    _            _      |               |      _            _ 
  827.  *   |              |     |               |     |              |
  828.  *   |  p   p   p   |     |  0    1    0  |     |  q   q   q   |
  829.  *   |   0   1   2  |  x  |               |  =  |   0   1   2  |
  830.  *   |_            _|     |               |     |_            _|
  831.  *                        |  Dx   Dy   1  |
  832.  *                        |               |
  833.  *                        |_             _|
  834.  *
  835.  *
  836.  */
  837.  
  838. void Translate(VECTOR p, double Dx, double Dy, VECTOR q)
  839. {
  840.    MATRIX t;
  841.  
  842.    t[0][0] = t[1][1] = t[2][2] = 1.0;
  843.    t[0][1] = t[0][2] = t[1][0] = t[1][2] = 0.0;
  844.    t[2][0] = Dx;
  845.    t[2][1] = Dy;
  846.  
  847.    VMmult(p, t, q);
  848. }
  849.  
  850.  
  851. /* TranslateObject() calls Translate() to translate all the vertices of
  852.  * the specified object by the amounts Dx and Dy.
  853.  */
  854. void TranslateObject(OBJECT *object, double Dx, double Dy)
  855. {
  856.    int i;
  857.    VECTOR p, q;
  858.  
  859.    p[2] = 1.0;
  860.    for (i = 0; i < object->numverts; i++)
  861.       {
  862.       p[0] = object->vertex[i].x;
  863.       p[1] = object->vertex[i].y;
  864.       Translate(p, Dx, Dy, q);
  865.       object->vertex[i].x = q[0];
  866.       object->vertex[i].y = q[1];
  867.       }
  868. }
  869.  
  870.  
  871. /* Rotate() rotates a point contained in VECTOR p about the point pt.
  872.  * The new point is contained in VECTOR q.
  873.  *
  874.  *   _               _  _                          _  _             _ 
  875.  *  |                 ||                            ||               |
  876.  *  |                 ||                            ||               |
  877.  *  |   1      0    0 ||  cos(angle)  sin(angle)  0 ||  1     0    0 |
  878.  *  |                 ||                            ||               |
  879.  *  |                 ||                            ||               |
  880.  *  |   0      1    0 || -sin(angle)  cos(angle)  0 ||  0     1    0 |
  881.  *  |                 ||                            ||               |
  882.  *  |                 ||                            ||               |
  883.  *  | -pt.x  -pt.y  1 ||       0          0       1 || pt.x  pt.y  1 |
  884.  *  |                 ||                            ||               |
  885.  *  |_               _||_                          _||_             _|
  886.  *
  887.  *
  888.  *  trans pt to origin            rotate              trans back to pt
  889.  *
  890.  */
  891.  
  892. void Rotate(VECTOR p, double angle, DPOINT pt, VECTOR q)
  893. {
  894.    MATRIX r, t, u;
  895.  
  896.    /* Translate point to origin */
  897.  
  898.    t[0][0] = t[1][1] = t[2][2] = 1.0;
  899.    t[0][1] = t[0][2] = t[1][0] = t[1][2] = 0.0;
  900.    t[2][0] = -pt.x;
  901.    t[2][1] = -pt.y;
  902.  
  903.    /* Define the rotation matrix.  Note that angle is in radians. */
  904.  
  905.    r[0][2] = r[1][2] = r[2][0] = r[2][1] = 0.0;
  906.    r[2][2] = 1.0;
  907.    r[0][0] = r[1][1] = cos(angle);
  908.    r[0][1] = sin(angle);
  909.    r[1][0] = -r[0][1];
  910.  
  911.    MMmult(t, r, u);
  912.  
  913.    /* Translate back to point */
  914.  
  915.    t[2][0] = pt.x;
  916.    t[2][1] = pt.y;
  917.  
  918.    MMmult(u, t, r);
  919.  
  920.    /* r now contains the accumulated matrix for the rotation */
  921.  
  922.    VMmult(p, r, q);
  923. }
  924.  
  925.  
  926. /* RotateObject() calls Translate() to translate all the vertices of
  927.  * the specified object by the amounts Dx and Dy.
  928.  */
  929. void RotateObject(OBJECT *object, double degrees)
  930. {
  931.    int i;
  932.    VECTOR p, q;
  933.    DPOINT center;
  934.    double radians = degrees * (M_PI / 180.0);
  935.  
  936.    /* Calculate the center of the object */
  937.  
  938.    center.x = center.y = 0.0;
  939.    for (i = 0; i < object->numverts; i++)
  940.       {
  941.       center.x += object->vertex[i].x;
  942.       center.y += object->vertex[i].y;
  943.       }
  944.    center.x /= object->numverts;
  945.    center.y /= object->numverts;
  946.  
  947.    p[2] = 1.0;
  948.    for (i = 0; i < object->numverts; i++)
  949.       {
  950.       p[0] = object->vertex[i].x;
  951.       p[1] = object->vertex[i].y;
  952.       Rotate(p, radians, center, q);
  953.       object->vertex[i].x = q[0];
  954.       object->vertex[i].y = q[1];
  955.       }
  956. }
  957.  
  958.  
  959. /* PointInExtent() returns a boolean value specifying whether a point
  960.  * is in a certain extent.
  961.  */
  962. int PointInExtent(POINT pt, EXTENT ext)
  963. {
  964.    return ext[0].x <= pt.x && pt.x <= ext[1].x &&
  965.           ext[0].y <= pt.y && pt.y <= ext[1].y;
  966. }
  967.  
  968.  
  969. /* PickCorrInMenu() performs pick correlation within the menu box area. */
  970.  
  971. int PickCorrInMenu(POINT pt)
  972. {
  973.    int i;
  974.  
  975.    for (i = 0; i < 8; i++)
  976.       if (PointInExtent(pt, menuBox[i].extent))
  977.          return i;
  978.  
  979.    return IGNORE;
  980. }
  981.  
  982.  
  983. /* HandlePickInMenu() calls the appropriate function to handle a menu
  984.  * option chosen by the user.
  985.  */
  986. int HandlePickInMenu(POINT pt)
  987. {
  988.    int choice;
  989.  
  990.    switch (choice = PickCorrInMenu(pt))
  991.       {
  992.       case IGNORE:
  993.          Beep();
  994.          break;
  995.  
  996.       case CREATE:
  997.          DoCreateObject();
  998.          break;
  999.  
  1000.       case DELETE:
  1001.          DoDeleteObject();
  1002.          break;
  1003.  
  1004.       case MOVE:
  1005.          DoMoveObject();
  1006.          break;
  1007.  
  1008.       case ROTATE:
  1009.          DoRotateObject();
  1010.          break;
  1011.  
  1012.       case DELETE_ALL:
  1013.          DoDeleteAll();
  1014.          break;
  1015.  
  1016.       case PRINT_DATA:
  1017.          DoPrintData();
  1018.          break;
  1019.  
  1020.       case CANCEL:
  1021.          DoCancel();
  1022.          break;
  1023.  
  1024.       case QUIT:
  1025.          choice = DoQuit();
  1026.          break;
  1027.       }
  1028.  
  1029.    return choice;
  1030. }
  1031.  
  1032.  
  1033. /* Message() prints a message in the message area of screen. */
  1034.  
  1035. void Message(char *msg)
  1036. {
  1037.    struct viewporttype view;
  1038.    int color;
  1039.    int msgx = mminx + 15;
  1040.    int msgy = (mminy + mmaxy) / 2;
  1041.    char temp[50];
  1042.  
  1043.    MouseOff();
  1044.  
  1045.    /* Get attributes */
  1046.  
  1047.    getviewsettings(&view);
  1048.    color = getcolor();
  1049.  
  1050.    setviewport(0, 0, maxx, maxy, CLIPPING_OFF);   /* Entire screen */
  1051.    settextjustify(LEFT_TEXT, CENTER_TEXT);
  1052.  
  1053.    memset(temp, 219, 49);  /* Character 219 is a block */
  1054.    temp[49] = '\0';
  1055.    setcolor(CYAN);
  1056.    moveto(msgx, msgy);
  1057.    outtext(temp);       /* Blank out existing message */
  1058.  
  1059.    setcolor(BLACK);
  1060.    moveto(msgx, msgy);
  1061.    outtext(msg);        /* Print message */
  1062.  
  1063.    /* Reset attributes */
  1064.  
  1065.    setviewport(view.left, view.top, view.right, view.bottom, view.clip);
  1066.    setcolor(color);
  1067.  
  1068.    MouseOn();
  1069. }
  1070.  
  1071.  
  1072. /* HighlightMenu() highlights the specified menu box. */
  1073.  
  1074. void HighlightMenu(int box, int background, int foreground)
  1075. {
  1076.    struct viewporttype view;
  1077.    struct fillsettingstype fill;
  1078.    int color;
  1079.    int bminx = menuBox[box].extent[0].x;
  1080.    int bminy = menuBox[box].extent[0].y;
  1081.    int bmaxx = menuBox[box].extent[1].x;
  1082.    int bmaxy = menuBox[box].extent[1].y;
  1083.  
  1084.    MouseOff();
  1085.  
  1086.    /* Get attributes */
  1087.  
  1088.    getviewsettings(&view);
  1089.    getfillsettings(&fill);
  1090.    color = getcolor();
  1091.  
  1092.    setviewport(bminx, bminy, bmaxx, bmaxy, CLIPPING_OFF);
  1093.    clearviewport();
  1094.  
  1095.    setviewport(0, 0, maxx, maxy, CLIPPING_OFF);
  1096.    rectangle(bminx, bminy, bmaxx, bmaxy);
  1097.  
  1098.    setfillstyle(SOLID_FILL, background);
  1099.    floodfill(bminx+1, bminy+1, WHITE);
  1100.  
  1101.    settextjustify(CENTER_TEXT, CENTER_TEXT);
  1102.    setcolor(foreground);
  1103.    moveto((bminx + bmaxx) / 2, (bminy + bmaxy) / 2);
  1104.    outtext(menuBox[box].text);
  1105.  
  1106.    /* Reset attributes */
  1107.  
  1108.    setviewport(view.left, view.top, view.right, view.bottom, view.clip);
  1109.    setfillstyle(fill.pattern, fill.color);
  1110.    setcolor(color);
  1111.  
  1112.    MouseOn();
  1113. }
  1114.  
  1115.  
  1116. /* HighlightSymbol() highlights the specified symbol chosen by the user
  1117.  * during the CREATE process.
  1118.  */
  1119. void HighlightSymbol(int symbol, int fillstyle, int color)
  1120. {
  1121.    struct viewporttype view;
  1122.    struct fillsettingstype fill;
  1123.  
  1124.    MouseOff();
  1125.    getviewsettings(&view);
  1126.    getfillsettings(&fill);
  1127.  
  1128.    setviewport(0, 0, maxx, maxy, CLIPPING_OFF);
  1129.    setfillstyle(fillstyle, color);
  1130.    if (symbol == TRIANGLE)
  1131.       floodfill(triangleCenter.x, triangleCenter.y, WHITE);
  1132.    else  /* SQUARE */
  1133.       floodfill(squareCenter.x, squareCenter.y, WHITE);
  1134.  
  1135.    setviewport(view.left, view.top, view.right, view.bottom, view.clip);
  1136.    setfillstyle(fill.pattern, fill.color);
  1137.    MouseOn();
  1138. }
  1139.  
  1140.  
  1141. /* DialogBox() displays a YES/NO dialog box, receives the input from
  1142.  * the user, restores the screen, and returns either YES or NO to the
  1143.  * caller.
  1144.  */
  1145. int DialogBox()
  1146. {
  1147.    POINT position;
  1148.    int choice, done = FALSE;
  1149.    struct viewporttype view;
  1150.  
  1151.    MouseOff();
  1152.  
  1153.    getviewsettings(&view);
  1154.    setviewport(0, 0, maxx, maxy, CLIPPING_OFF);   /* Entire screen */
  1155.  
  1156.    /* Save the screen area under where the dialog box will display */
  1157.    saveBuffer = malloc(dialogSize);
  1158.    getimage(dminx, dminy, dmaxx, dmaxy, saveBuffer);
  1159.  
  1160.    /* Display the dialog box */
  1161.    putimage(dminx, dminy, dialogBuffer, COPY_PUT);
  1162.  
  1163.    MouseOn();
  1164.  
  1165.    Message("Choose YES or NO using left mouse button");
  1166.  
  1167.    do
  1168.       {
  1169.       MouseWaitForPress(LEFTBUTTON, &position);
  1170.       MouseWaitForRelease(LEFTBUTTON, &position);
  1171.       if (PointInExtent(position, yesExtent))
  1172.          {
  1173.          choice = YES;
  1174.          done = TRUE;
  1175.          }
  1176.       else if (PointInExtent(position, noExtent))
  1177.          {
  1178.          choice = NO;
  1179.          done = TRUE;
  1180.          }
  1181.       }
  1182.    while (!done);
  1183.  
  1184.    /* Restore the screen area and free the buffer */
  1185.  
  1186.    MouseOff();
  1187.    putimage(dminx, dminy, saveBuffer, COPY_PUT);
  1188.    free(saveBuffer);
  1189.    MouseOn();
  1190.  
  1191.    setviewport(view.left, view.top, view.right, view.bottom, view.clip);
  1192.    return choice;
  1193. }
  1194.  
  1195.  
  1196. /* AddList() adds an object to the linked list. */
  1197.  
  1198. NODEPTR AddList(OBJECT object)
  1199. {
  1200.    NODEPTR newnode;
  1201.  
  1202.    newnode = (NODEPTR) malloc(sizeof(NODE));
  1203.    if (newnode)
  1204.       {
  1205.       newnode->object = object;
  1206.       newnode->next = NULL;
  1207.       if (!list)
  1208.          list = listtail = newnode;
  1209.       else
  1210.          listtail = listtail->next = newnode;
  1211.       }
  1212.    return newnode;
  1213. }
  1214.  
  1215.  
  1216. /* DeleteList() deletes an object from the linked list. */
  1217.  
  1218. void DeleteList(NODEPTR ptr)
  1219. {
  1220.    NODEPTR p;
  1221.  
  1222.    if (ptr == list)
  1223.       list = ptr->next;
  1224.    else if (ptr == listtail)
  1225.       {
  1226.       for (p = list; p->next != listtail; p = p->next)
  1227.          ;
  1228.       p->next = NULL;
  1229.       listtail = p;
  1230.       }
  1231.    else
  1232.       {
  1233.       for (p = list; p->next != ptr; p = p->next)
  1234.          ;
  1235.       p->next = ptr->next;
  1236.       }
  1237.    free(ptr);
  1238. }
  1239.  
  1240.  
  1241. /* SearchList() searches the list and returns a pointer to the object
  1242.  * in the list if the point lies in the extent of the object.  If no
  1243.  * object is found, NULL is returned.
  1244.  */
  1245. NODEPTR SearchList(POINT pt)
  1246. {
  1247.    NODEPTR p;
  1248.    VECTOR vcoords[4], wcoords[4];
  1249.    EXTENT extent;
  1250.    int i, x, y;
  1251.  
  1252.    for (p = list; p; p = p->next)
  1253.       {
  1254.       /* Transform the object's vertices into physical device
  1255.        * coordinates and calculate its extent.
  1256.        */
  1257.       extent[0].x = maxx;
  1258.       extent[0].y = maxy;
  1259.       extent[1].x = 0;
  1260.       extent[1].y = 0;
  1261.       for (i = 0; i < p->object.numverts; i++)
  1262.          {
  1263.          wcoords[i][0] = p->object.vertex[i].x;
  1264.          wcoords[i][1] = p->object.vertex[i].y;
  1265.          wcoords[i][2] = 1.0;
  1266.          VMmult(wcoords[i], W2V, vcoords[i]);
  1267.          vcoords[i][0] += vminx;
  1268.          vcoords[i][1] += vminy;
  1269.          if ((x = (int) vcoords[i][0]) < extent[0].x) extent[0].x = x;
  1270.          if ((y = (int) vcoords[i][1]) < extent[0].y) extent[0].y = y;
  1271.          if ((x = (int) vcoords[i][0]) > extent[1].x) extent[1].x = x;
  1272.          if ((y = (int) vcoords[i][1]) > extent[1].y) extent[1].y = y;
  1273.          }
  1274.       if (PointInExtent(pt, extent))
  1275.          return p;   /* Object was found */
  1276.       }
  1277.  
  1278.    return NULL;   /* Object was not found at that position */
  1279. }
  1280.  
  1281.  
  1282. /* DoCreateObject() handles the CREATE menu option. */
  1283.  
  1284. void DoCreateObject()
  1285. {
  1286.    POINT position;
  1287.    VECTOR vcoords, wcoords;
  1288.    OBJECT object;
  1289.    int symbol, normalColor;
  1290.  
  1291.    HighlightMenu(CREATE, LIGHTGRAY, BLACK);
  1292.    Message("Choose a TRIANGLE or SQUARE with left button");
  1293.  
  1294.    while (1)
  1295.       {
  1296.       MouseWaitForPress(LEFTBUTTON, &position);
  1297.       MouseWaitForRelease(LEFTBUTTON, &position);
  1298.       if (PointInExtent(position, menuBox[CANCEL].extent))
  1299.          {
  1300.          HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
  1301.          HighlightMenu(CREATE, RED, WHITE);
  1302.          HighlightMenu(CANCEL, RED, WHITE);
  1303.          return;
  1304.          }
  1305.       if (PointInExtent(position, triangleExtent))
  1306.          {
  1307.          symbol = TRIANGLE;
  1308.          break;
  1309.          }
  1310.       else if (PointInExtent(position, squareExtent))
  1311.          {
  1312.          symbol = SQUARE;
  1313.          break;
  1314.          }
  1315.       else
  1316.          Beep();  /* Invalid click */
  1317.       }
  1318.  
  1319.    normalColor = (symbol == TRIANGLE) ? MAGENTA : GREEN;
  1320.    HighlightSymbol(symbol, INTERLEAVE_FILL, LIGHTGRAY);
  1321.    Message("Choose location in \"room\" with left button");
  1322.  
  1323.    while (1)
  1324.       {
  1325.       MouseWaitForPress(LEFTBUTTON, &position);
  1326.       MouseWaitForRelease(LEFTBUTTON, &position);
  1327.       if (PointInExtent(position, menuBox[CANCEL].extent))
  1328.          {
  1329.          HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
  1330.          HighlightSymbol(symbol, SOLID_FILL, normalColor);
  1331.          HighlightMenu(CREATE, RED, WHITE);
  1332.          HighlightMenu(CANCEL, RED, WHITE);
  1333.          return;
  1334.          }
  1335.       if (PointInExtent(position, roomExtent))
  1336.          break;
  1337.       else
  1338.          Beep();  /* Invalid click */
  1339.       }
  1340.  
  1341.    vcoords[0] = position.x - vminx;
  1342.    vcoords[1] = position.y - vminy;
  1343.    vcoords[2] = 1.0;
  1344.    VMmult(vcoords, V2W, wcoords);
  1345.  
  1346.    /* wcoords is the bottom left corner of the object */
  1347.  
  1348.    object.vertex[0].x = wcoords[0];
  1349.    object.vertex[0].y = wcoords[1];
  1350.    object.vertex[1].x = wcoords[0] + 1.0;
  1351.    object.vertex[1].y = wcoords[1];
  1352.  
  1353.    if (symbol == TRIANGLE)
  1354.       {
  1355.       object.vertex[2].x = wcoords[0] + 0.5;
  1356.       object.vertex[2].y = wcoords[1] + 1.0;
  1357.       object.numverts = 3;
  1358.       }
  1359.    else  /* SQUARE */
  1360.       {
  1361.       object.vertex[2].x = wcoords[0] + 1.0;
  1362.       object.vertex[2].y = wcoords[1] + 1.0;
  1363.       object.vertex[3].x = wcoords[0];
  1364.       object.vertex[3].y = wcoords[1] + 1.0;
  1365.       object.numverts = 4;
  1366.       }
  1367.  
  1368.    AddList(object);
  1369.  
  1370.    MouseOff();
  1371.    DrawObject(object);
  1372.    MouseOn();
  1373.  
  1374.    HighlightSymbol(symbol, SOLID_FILL, normalColor);
  1375.    HighlightMenu(CREATE, RED, WHITE);
  1376. }
  1377.  
  1378.  
  1379. /* DoDeleteObject() handles the DELETE menu option. */
  1380.  
  1381. void DoDeleteObject()
  1382. {
  1383.    POINT position;
  1384.    NODEPTR p, ptr;
  1385.    char *mainMessage = "Select the object to be deleted using left button";
  1386.  
  1387.    HighlightMenu(DELETE, LIGHTGRAY, BLACK);
  1388.  
  1389.    if (!list)  /* No objects to be deleted */
  1390.       {
  1391.       Beep();
  1392.       Message("No objects to be deleted");
  1393.       sleep(1);
  1394.       HighlightMenu(DELETE, RED, WHITE);
  1395.       return;
  1396.       }
  1397.  
  1398.    Message(mainMessage);
  1399.  
  1400.    while (1)
  1401.       {
  1402.       MouseWaitForPress(LEFTBUTTON, &position);
  1403.       MouseWaitForRelease(LEFTBUTTON, &position);
  1404.       if (PointInExtent(position, menuBox[CANCEL].extent))
  1405.          {
  1406.          HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
  1407.          HighlightMenu(DELETE, RED, WHITE);
  1408.          HighlightMenu(CANCEL, RED, WHITE);
  1409.          return;
  1410.          }
  1411.       if ((ptr = SearchList(position)) == NULL)
  1412.          {
  1413.          Beep();
  1414.          Message("No object found there");
  1415.          sleep(1);
  1416.          Message(mainMessage);
  1417.          }
  1418.       else
  1419.          break;   /* Object found */
  1420.       }
  1421.  
  1422.    /* Erase the object */
  1423.    MouseOff();
  1424.    setcolor(BLUE);
  1425.    DrawObject(ptr->object);
  1426.    setcolor(WHITE);
  1427.  
  1428.    /* Delete it from the list */
  1429.    DeleteList(ptr);
  1430.  
  1431.    /* Redraw all objects in case there was an overlap */
  1432.    for (p = list; p; p = p->next)
  1433.       DrawObject(p->object);
  1434.    MouseOn();
  1435.  
  1436.    HighlightMenu(DELETE, RED, WHITE);
  1437. }
  1438.  
  1439.  
  1440. /* DoMoveObject() handles the MOVE menu option. */
  1441.  
  1442. void DoMoveObject()
  1443. {
  1444.    POINT position;
  1445.    NODEPTR p, ptr;
  1446.    VECTOR vcoords, wcoords;
  1447.    int i, valid;
  1448.    double Dx, Dy, deltaX, deltaY;
  1449.    int buttonPressed;
  1450.    char *mainMessage = "Drag an object with left button, then release";
  1451.  
  1452.    HighlightMenu(MOVE, LIGHTGRAY, BLACK);
  1453.  
  1454.    if (!list)  /* No objects to be deleted */
  1455.       {
  1456.       Beep();
  1457.       Message("No objects to be moved");
  1458.       sleep(1);
  1459.       HighlightMenu(MOVE, RED, WHITE);
  1460.       return;
  1461.       }
  1462.  
  1463.    Message(mainMessage);
  1464.  
  1465.    while (1)
  1466.       {
  1467.       MouseWaitForPress(LEFTBUTTON, &position);
  1468.       if (PointInExtent(position, menuBox[CANCEL].extent))
  1469.          {
  1470.          MouseWaitForRelease(LEFTBUTTON, &position);
  1471.          HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
  1472.          HighlightMenu(MOVE, RED, WHITE);
  1473.          HighlightMenu(CANCEL, RED, WHITE);
  1474.          return;
  1475.          }
  1476.       if ((ptr = SearchList(position)) == NULL)
  1477.          {
  1478.          Beep();
  1479.          Message("No object found there");
  1480.          sleep(1);
  1481.          Message(mainMessage);
  1482.          MouseWaitForRelease(LEFTBUTTON, &position);
  1483.          }
  1484.       else
  1485.          break;   /* Object found */
  1486.       }
  1487.  
  1488.    /* Determine x and y distance from clicked position to the bottom
  1489.     * left corner of the object (deltaX and deltaY).  This will be used
  1490.     * below so that when dragging the object, the cursor will remain on
  1491.     * the object at the same place.
  1492.     */
  1493.    vcoords[0] = position.x - vminx;
  1494.    vcoords[1] = position.y - vminy;
  1495.    vcoords[2] = 1.0;
  1496.    VMmult(vcoords, V2W, wcoords);
  1497.    deltaX = wcoords[0] - ptr->object.vertex[0].x;
  1498.    deltaY = wcoords[1] - ptr->object.vertex[0].y;
  1499.  
  1500.    /* Restrict the mouse to the "room" */
  1501.  
  1502.    MouseRestrict();
  1503.  
  1504.    do
  1505.       {
  1506.       buttonPressed = MouseStatus(&position);
  1507.  
  1508.       /* Convert the clicked position to world coordinates */
  1509.       vcoords[0] = position.x - vminx;
  1510.       vcoords[1] = position.y - vminy;
  1511.       vcoords[2] = 1.0;
  1512.       VMmult(vcoords, V2W, wcoords);
  1513.  
  1514.       /* Determine Dx and Dy for the translation */
  1515.       Dx = wcoords[0] - ptr->object.vertex[0].x - deltaX;
  1516.       Dy = wcoords[1] - ptr->object.vertex[0].y - deltaY;
  1517.  
  1518.       /* Erase old position */
  1519.       MouseOff();
  1520.       setcolor(BLUE);
  1521.       DrawObject(ptr->object);
  1522.       setcolor(WHITE);
  1523.  
  1524.       /* Translate the object and redraw all objects */
  1525.       TranslateObject(&ptr->object, Dx, Dy);
  1526.       for (p = list; p; p = p->next)
  1527.          DrawObject(p->object);
  1528.       MouseOn();
  1529.       }
  1530.    while (buttonPressed & LEFTBUTTON); /* Wait for release */
  1531.  
  1532.    MouseFree();
  1533.  
  1534.    HighlightMenu(MOVE, RED, WHITE);
  1535. }
  1536.  
  1537.  
  1538. /* DoRotateObject() handles the ROTATE menu option. */
  1539.  
  1540. void DoRotateObject()
  1541. {
  1542.    POINT position;
  1543.    NODEPTR p, ptr;
  1544.    int buttonPressed;
  1545.    double degrees;
  1546.    char *mainMessage = "Select an object to be rotated using left button";
  1547.  
  1548.    HighlightMenu(ROTATE, LIGHTGRAY, BLACK);
  1549.  
  1550.    if (!list)  /* No objects to be rotated */
  1551.       {
  1552.       Beep();
  1553.       Message("No objects to be rotated");
  1554.       sleep(1);
  1555.       HighlightMenu(ROTATE, RED, WHITE);
  1556.       return;
  1557.       }
  1558.  
  1559.    Message(mainMessage);
  1560.  
  1561.    while (1)
  1562.       {
  1563.       MouseWaitForPress(LEFTBUTTON, &position);
  1564.       MouseWaitForRelease(LEFTBUTTON, &position);
  1565.       if (PointInExtent(position, menuBox[CANCEL].extent))
  1566.          {
  1567.          HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
  1568.          HighlightMenu(ROTATE, RED, WHITE);
  1569.          HighlightMenu(CANCEL, RED, WHITE);
  1570.          return;
  1571.          }
  1572.       if ((ptr = SearchList(position)) == NULL)
  1573.          {
  1574.          Beep();
  1575.          Message("No object found there");
  1576.          sleep(1);
  1577.          Message(mainMessage);
  1578.          }
  1579.       else
  1580.          break;
  1581.       }
  1582.  
  1583.    Message("Use left/right buttons to rotate, QUIT to end");
  1584.  
  1585.    while (1)
  1586.       {
  1587.       buttonPressed = MouseStatus(&position);
  1588.       if (buttonPressed)
  1589.          {
  1590.          if (PointInExtent(position, menuBox[QUIT].extent))
  1591.             {
  1592.             HighlightMenu(QUIT, LIGHTGRAY, BLACK);
  1593.             HighlightMenu(ROTATE, RED, WHITE);
  1594.             HighlightMenu(QUIT, RED, WHITE);
  1595.             MouseWaitForRelease(LEFTBUTTON, &position);
  1596.             return;
  1597.             }
  1598.  
  1599.          if (buttonPressed & LEFTBUTTON)
  1600.             degrees = 2.0;
  1601.          else
  1602.             degrees = -2.0;
  1603.  
  1604.          /* Erase old position */
  1605.  
  1606.          MouseOff();
  1607.          setcolor(BLUE);
  1608.          DrawObject(ptr->object);
  1609.          setcolor(WHITE);
  1610.  
  1611.          /* Rotate the object and redraw all objects */
  1612.  
  1613.          RotateObject(&ptr->object, degrees);
  1614.          for (p = list; p; p = p->next)
  1615.             DrawObject(p->object);
  1616.          MouseOn();
  1617.          }
  1618.       }
  1619. }
  1620.  
  1621.  
  1622. /* DoDeleteAll() handles the DELETE ALL menu option. */
  1623.  
  1624. void DoDeleteAll()
  1625. {
  1626.    NODEPTR p, q;
  1627.  
  1628.    HighlightMenu(DELETE_ALL, LIGHTGRAY, BLACK);
  1629.  
  1630.    if (!list)  /* No objects to be deleted */
  1631.       {
  1632.       Beep();
  1633.       Message("No objects to be deleted");
  1634.       sleep(1);
  1635.       HighlightMenu(DELETE_ALL, RED, WHITE);
  1636.       return;
  1637.       }
  1638.  
  1639.    /* After obtaining confirmation through the dialog box, erase
  1640.     * all objects, free the memory, and make the list null.
  1641.     */
  1642.  
  1643.    if (DialogBox() == YES)
  1644.       {
  1645.       MouseOff();
  1646.       setcolor(BLUE);
  1647.       p = list;
  1648.       while (p)
  1649.          {
  1650.          q = p;
  1651.          DrawObject(p->object);
  1652.          p = p->next;
  1653.          free(q);
  1654.          }
  1655.       list = listtail = NULL;
  1656.       setcolor(WHITE);
  1657.       MouseOn();
  1658.       }
  1659.  
  1660.    HighlightMenu(DELETE_ALL, RED, WHITE);
  1661. }
  1662.  
  1663.  
  1664. /* DoPrintData() handles the PRINT DATA menu option. */
  1665.  
  1666. void DoPrintData()
  1667. {
  1668.    FILE *outfile;
  1669.    NODEPTR p;
  1670.    int i;
  1671.  
  1672.    HighlightMenu(PRINT_DATA, LIGHTGRAY, BLACK);
  1673.  
  1674.    if ((outfile = fopen("proj1.dat", "w")) == NULL)
  1675.       {
  1676.       Message("Cannot create file PROJ1.DAT");
  1677.       HighlightMenu(PRINT_DATA, RED, WHITE);
  1678.       return;
  1679.       }
  1680.  
  1681.    if (!list)
  1682.       fprintf(outfile, "No objects found");
  1683.    else
  1684.       for (p = list; p; p = p->next)
  1685.          {
  1686.          fprintf(outfile, "Object Type: %s\n",
  1687.             p->object.numverts == 4 ? "SQUARE" : "TRIANGLE");
  1688.          fprintf(outfile, "Vertices:\n");
  1689.          for (i = 0; i < p->object.numverts; i++)
  1690.             fprintf(outfile, "  (%2.2f, %2.2f)\n",
  1691.                p->object.vertex[i].x, p->object.vertex[i].y);
  1692.          fprintf(outfile, "\n");
  1693.          }
  1694.  
  1695.    fclose(outfile);
  1696.  
  1697.    Message("File PROJ1.DAT successfully created");
  1698.    sleep(1);
  1699.  
  1700.    HighlightMenu(PRINT_DATA, RED, WHITE);
  1701. }
  1702.  
  1703.  
  1704. /* DoCancel() handles the CANCEL menu option.  Note that since CANCEL
  1705.  * must be used in conjunction with another menu option, this option
  1706.  * chosen as a standalone function just beeps and returns an error message.
  1707.  */
  1708. void DoCancel()
  1709. {
  1710.    HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
  1711.    Beep();
  1712.    Message("Nothing to cancel!");
  1713.    sleep(1);
  1714.    HighlightMenu(CANCEL, RED, WHITE);
  1715. }
  1716.  
  1717.  
  1718. /* DoQuit() handles the QUIT menu option. */
  1719.  
  1720. int DoQuit()
  1721. {
  1722.    int choice;
  1723.  
  1724.    HighlightMenu(QUIT, LIGHTGRAY, BLACK);
  1725.    choice = DialogBox();
  1726.    HighlightMenu(QUIT, RED, WHITE);
  1727.    return (choice == YES) ? QUIT : IGNORE;
  1728. }
  1729.